#include "ObjectLoader.h"
#include "myConstants.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <assert.h>
#include "Vertex.h"

#include <cstdlib>

using namespace std;

ObjectLoader::ObjectLoader(const char* path) {
	objFilePath = path;
}
void ObjectLoader::loadObject(GLTriangleBatch* objectBatch) {
	cout << "entered load...";

	// clear everything up just in case
	spatialCoordinates.clear();
	textures.clear();
	normals.clear();
	allTriangles.clear();

	ifstream in(objFilePath);
	string s;

	while(getline(in, s)) {
		vector<string> words;
		breakIntoWords(s, &words);

		if ((words[0].compare("#")) == 0) {

			continue;

		} else if ((words[0].compare("v")) == 0) {

			readSpatialCoordinates(&words);

		} else if ((words[0].compare("vt")) == 0) {

			readTextureCoordinates(&words);

		} else if ((words[0].compare("vn")) == 0) {

			readNormalCoordinates(&words);
	
		} else if (words[0].compare("f") == 0) {

			readATriangle(&words);
			//printLineWords(&words);
		}
	}

	M3DVector3f vVertex[4];
	M3DVector2f vTexture[4];
	M3DVector3f vNormal[4];

	objectBatch->BeginMesh(N_MAX_VERTS);

	for (unsigned int i = 0; i < allTriangles.size(); ++i) {
		prepareParameters(allTriangles[i], vVertex, vTexture, vNormal);
		objectBatch->AddTriangle(vVertex, vNormal, vTexture);
	}
	objectBatch->End();
}
void ObjectLoader::readSpatialCoordinates(vector<string> *words) {
	assert((*words).size() >= 4);

	SpatialCoordinate coord((float)atof((*words)[1].c_str()), (float)atof((*words)[2].c_str()), (float)atof((*words)[3].c_str()));

	this->spatialCoordinates.push_back(coord);
}
void ObjectLoader::readTextureCoordinates(vector<string> *words) {
	assert((*words).size() >= 3);

	Texture aTexture((float)atof((*words)[1].c_str()), (float)atof((*words)[2].c_str()));

	this->textures.push_back(aTexture);
}
void ObjectLoader::readNormalCoordinates(vector<string> *words) {
	assert((*words).size() >= 3);

	Normal aNormal((float)atof((*words)[1].c_str()), (float)atof((*words)[2].c_str()), (float)atof((*words)[3].c_str()));

	this->normals.push_back(aNormal);
}
void ObjectLoader::readATriangle(std::vector<std::string> *words) {
	Triangle aTriangle;
	for (unsigned int i = 1; i < (*words).size(); ++i) {
		vector<int> indices;
		splitWord(&indices, (*words)[i]);
		
		int spatialCoordIndex = indices[0] - 1;
		SpatialCoordinate sc(spatialCoordinates[spatialCoordIndex].getX(),
							 spatialCoordinates[spatialCoordIndex].getY(),
							 spatialCoordinates[spatialCoordIndex].getZ());
		Texture tex;
		if (textures.size() > 0) {
			int textureCoordIndex = indices[1] - 1;
			tex.setX(textures[textureCoordIndex].getX());
			tex.setY(textures[textureCoordIndex].getY());
		}

		int normalCoordIndex = indices[2] - 1;
		Normal norm(normals[normalCoordIndex].getX(),
					normals[normalCoordIndex].getY(),
					normals[normalCoordIndex].getZ());
		
		Vertex aVertex(sc, tex, norm);

		aTriangle.addVertex(aVertex);
	}
	this->allTriangles.push_back(aTriangle);
}
void ObjectLoader::prepareParameters(Triangle& aTriangle, M3DVector3f* vVertex, M3DVector2f* vTexture, M3DVector3f* vNormal) {
	vector<Vertex>& triangleVertices = aTriangle.getVertices();
	for (unsigned int i = 0; i < triangleVertices.size(); ++i) {
		vVertex[i][0] = triangleVertices[i].getSpatialCoordinate().getX();
		vVertex[i][1] = triangleVertices[i].getSpatialCoordinate().getY();
		vVertex[i][2] = triangleVertices[i].getSpatialCoordinate().getZ();

		vTexture[i][0] = triangleVertices[i].getTexture().getX();
		vTexture[i][1] = triangleVertices[i].getTexture().getY();

		vNormal[i][0] = triangleVertices[i].getNormal().getX();
		vNormal[i][1] = triangleVertices[i].getNormal().getY();
		vNormal[i][2] = triangleVertices[i].getNormal().getZ();
	}
}
void ObjectLoader::breakIntoWords(string& line, vector<string> *words) {
	string word;
	istringstream iss(line);
	while (iss >> word) {
		words->push_back(word);
	}
}
void ObjectLoader::drawHalfOfSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks) {
    GLfloat drho = (GLfloat)(3.141592653589) / (GLfloat) iStacks;
    GLfloat dtheta = 2.0f * (GLfloat)(3.141592653589) / (GLfloat) iSlices;
	GLfloat ds = 1.0f / (GLfloat) iSlices;
	GLfloat dt = 1.0f / (GLfloat) iStacks;
	GLfloat t = 1.0f;	
	GLfloat s = 0.0f;
    GLint i, j;     // Looping variables
    
    sphereBatch.BeginMesh(iSlices * iStacks * 6);
	for (i = 0; i < iStacks; i++) {
		if (i >= iStacks / 2) {
			break;
		}
		GLfloat rho = (GLfloat)i * drho;
		GLfloat srho = (GLfloat)(sin(rho));
		GLfloat crho = (GLfloat)(cos(rho));
		GLfloat srhodrho = (GLfloat)(sin(rho + drho));
		GLfloat crhodrho = (GLfloat)(cos(rho + drho));
		
        // Many sources of OpenGL sphere drawing code uses a triangle fan
        // for the caps of the sphere. This however introduces texturing 
        // artifacts at the poles on some OpenGL implementations
        s = 0.0f;
		M3DVector3f vVertex[4];
		M3DVector3f vNormal[4];
		M3DVector2f vTexture[4];

		for ( j = 0; j < iSlices; j++) {
			GLfloat theta = (j == iSlices) ? 0.0f : j * dtheta;
			GLfloat stheta = (GLfloat)(-sin(theta));
			GLfloat ctheta = (GLfloat)(cos(theta));
			
			GLfloat x = stheta * srho;
			GLfloat y = ctheta * srho;
			GLfloat z = crho;
        
			vTexture[0][0] = s;
			vTexture[0][1] = t;
			vNormal[0][0] = x;
			vNormal[0][1] = y;
			vNormal[0][2] = z;
			vVertex[0][0] = x * fRadius;
			vVertex[0][1] = y * fRadius;
			vVertex[0][2] = z * fRadius;
			
            x = stheta * srhodrho;
			y = ctheta * srhodrho;
			z = crhodrho;

 			vTexture[1][0] = s;
			vTexture[1][1] = t - dt;
			vNormal[1][0] = x;
			vNormal[1][1] = y;
			vNormal[1][2] = z;
			vVertex[1][0] = x * fRadius;
			vVertex[1][1] = y * fRadius;
			vVertex[1][2] = z * fRadius;
			

			theta = ((j+1) == iSlices) ? 0.0f : (j+1) * dtheta;
			stheta = (GLfloat)(-sin(theta));
			ctheta = (GLfloat)(cos(theta));
			
			x = stheta * srho;
			y = ctheta * srho;
			z = crho;
        
            s += ds;
			vTexture[2][0] = s;
			vTexture[2][1] = t;
			vNormal[2][0] = x;
			vNormal[2][1] = y;
			vNormal[2][2] = z;
			vVertex[2][0] = x * fRadius;
			vVertex[2][1] = y * fRadius;
			vVertex[2][2] = z * fRadius;
			
            x = stheta * srhodrho;
			y = ctheta * srhodrho;
			z = crhodrho;

 			vTexture[3][0] = s;
			vTexture[3][1] = t - dt;
			vNormal[3][0] = x;
			vNormal[3][1] = y;
			vNormal[3][2] = z;
			vVertex[3][0] = x * fRadius;
			vVertex[3][1] = y * fRadius;
			vVertex[3][2] = z * fRadius;
		
			sphereBatch.AddTriangle(vVertex, vNormal, vTexture);			
			/*
			// Rearrange for next triangle
			memcpy(vVertex[0], vVertex[1], sizeof(M3DVector3f));
			memcpy(vNormal[0], vNormal[1], sizeof(M3DVector3f));
			memcpy(vTexture[0], vTexture[1], sizeof(M3DVector2f));
			
			memcpy(vVertex[1], vVertex[3], sizeof(M3DVector3f));
			memcpy(vNormal[1], vNormal[3], sizeof(M3DVector3f));
			memcpy(vTexture[1], vTexture[3], sizeof(M3DVector2f));
					
			sphereBatch.AddTriangle(vVertex, vNormal, vTexture);
			*/
			}
        t -= dt;
        }
		sphereBatch.End();
}
void ObjectLoader::printLineWords(vector<string> *words) {

	for (unsigned int i = 0; i < (*words).size(); ++i) {
		cout << "word" << i << ": " << (*words)[i] << " ";
	}
	cout << endl;
}
void ObjectLoader::splitWord(std::vector<int> *indices, string& word) {
	stringstream ss(word);
	string segment;
	while (getline(ss, segment, '/')) {
		(*indices).push_back(atoi(segment.c_str()));
	}
}
void ObjectLoader::setPath(const char* filepath) {
	this->objFilePath = filepath;
}
// THIS IS SOME BACKED UP CODE IT MIGHT BE USEFULL LATER
/*
	cout << "Vertices:" << endl;
	for (unsigned int i = 0; i < this->vertices.size(); ++i) {
		cout << this->vertices[i].getX() << " " << this->vertices[i].getY() << " " << this->vertices[i].getZ() << endl;
	}
	cout << endl;
	cout << "Textures:" << endl;
	for (unsigned int i = 0; i < this->textures.size(); ++i) {
		cout << this->textures[i].getX() << " " << this->textures[i].getY() << endl;
	}
	cout << endl;
	cout << "Normals:" << endl;
	for (unsigned int i = 0; i < this->normals.size(); ++i) {
		cout << this->normals[i].getX() << " " << this->normals[i].getY() << " " << this->normals[i].getZ() << endl;
	}
*/
